/*
* ============================================================================
*  Name        : platform_win.h
*  Part of     : Finite state machine utilities
*  Description : Platform abstruction layer
*  Version     : %version: %
*  
*
* ============================================================================
*/
/**
 * @file sea_platform_win.h
 *
 * @ingroup Platforms
 */

#if !defined( __sea_platform_win_h__ )
#define __sea_platform_win_h__

#pragma warning( disable : 4355 )
#pragma warning( disable : 4189 )
#pragma warning( disable : 4201 )
#pragma warning( disable : 4127 )

#include <windows.h>
#include <string>
#include <vector>
#include <map>
#include <typeinfo>
#include <algorithm>
#include <stdlib.h>
#include <assert.h>
#include <time.h>
#include <conio.h>
#include <Mmsystem.h>

#include "sea_assert.h"

using namespace std;

namespace sea
{
    #define SEA_PRETTY_FUNCTION __FUNCSIG__

    typedef std::string String;
    typedef DWORD OsThreadHandle;

    /**
     * @class OsThread
     * @brief Thread in Windows
     *
     * @ingroup Platforms
     */
    class OsThread : public Object
    {
    public:

        OsThread( );
        ~OsThread( );

        virtual void exec( ) { }
        static DWORD entry( void* param )
        {
            {
                OsThread* the_thread = reinterpret_cast< OsThread* >( param );
                sea::ref< OsThread > thread = the_thread;

                the_thread->exec( );
                the_thread->m_started = false;
                the_thread->m_threadId = 0;
            }

            ExitThread( 0 );
        }
        
        static OsThreadHandle currentThreadId( ){ return GetCurrentThreadId( ); }
        void start( );
        void wait( i::uint32 time = -1 );
        OsThreadHandle id( ) { return m_threadId; }

        void setPriority( OsThreadPriority value )
        {
            if ( m_handle && value >= Lowest && value <= Highest )
            {
                SetThreadPriority( m_handle, m_native_priority[ value ] );
            }
        }
        bool isRunning( ){ return m_threadId != 0 && m_started; }

    private:
        static int m_native_priority[ ];
        HANDLE m_handle;
        OsThreadHandle m_threadId;
        bool m_started;
    };

    /**
     * @class OsSemaphore
     * @brief Semaphore in Windows
     *
     * @ingroup Platforms
     */
    class OsSemaphore : public Object
    {
    public:
        #define MAX_SEM_COUNT 0x7FFFFFFF

        OsSemaphore( int initCounter = 0 )
        {
            m_semaphore = CreateSemaphore( 
                NULL,           // default security attributes
                initCounter,    // initial count
                MAX_SEM_COUNT,  // maximum count
                NULL );         // unnamed semaphore
        }
        ~OsSemaphore( ) { CloseHandle( m_semaphore ); }

        virtual void signal( int increment = 1 ) { ReleaseSemaphore( m_semaphore, increment, NULL ); }

        virtual bool wait( int decrement = 1, DWORD timeout = INFINITE )
        {
            int counter = decrement;
            while ( decrement-- )
            {
                DWORD dwWaitResult;
                dwWaitResult = WaitForSingleObject( m_semaphore, timeout );
                if ( dwWaitResult == WAIT_OBJECT_0 ) // Signaled
                {
                    counter--;
                }
            }
            return counter == 0;
        }
    protected:
        HANDLE  m_semaphore;
    };

  
    /**
     * @class OsMutex
     * @brief Mutex in Windows
     *
     * @ingroup Platforms
     */
    class OsMutex
    {
    public:
        OsMutex( ){ InitializeCriticalSection ( &m_CriticalSection ); }
        ~OsMutex( ){ DeleteCriticalSection ( &m_CriticalSection ); }
        
        bool try_enter( ){ return TryEnterCriticalSection( &m_CriticalSection ) != 0; }
        void enter( ){ EnterCriticalSection( &m_CriticalSection ); }
        void leave( ){ LeaveCriticalSection( &m_CriticalSection ); }

        /**
         * @class Scoped
         * @brief Mutex scoped lock/unlock
         *
         * @ingroup Platforms
         */
        class Scoped
        {
        public:
            Scoped( OsMutex* critical_section, bool do_try = false );
            ~Scoped( );
        private:
            OsMutex* m_mutex;
        };
    private:
        CRITICAL_SECTION m_CriticalSection;
    };

    /**
     * @class OsSpinlock
     * @brief Spinlock in Windows
     *
     * @ingroup Platforms
     */
    class OsSpinlock
    {
    public:
        OsSpinlock( ) : m_thread_id( ( DWORD )-1 ), m_value( 0 ), m_count( 0 ) { }
        ~OsSpinlock( ) { }

        void enter( )
        {
            if ( m_thread_id == GetCurrentThreadId( ) )
            {
                m_count++;
                return;
            }

            while ( ::InterlockedCompareExchange( &m_value, 1, 0 ) )
            {
                // YieldProcessor( ) - doesnt work well !!!
                Sleep( 1 );
            }

            if ( m_thread_id == (DWORD)-1 )
            {
                m_thread_id = OsThread::currentThreadId( );
            }

            m_count++;
        }

        void leave( )
        {
            m_count--;

            if( m_count == 0 )
            {
                m_thread_id = (DWORD)-1;
                ::InterlockedExchange( &m_value, 0 );
            }
        }

        /**
         * @class Scoped
         * @brief Mutex scoped lock/unlock
         *
         * @ingroup Platforms
         */
        class Scoped
        {
        public:
            Scoped( OsSpinlock* critical_section ): m_lock( critical_section ) { if ( m_lock ) m_lock->enter( ); }
            ~Scoped( ){ if ( m_lock ) m_lock->leave( ); }
        private:
            OsSpinlock* m_lock;
        };
    private:
        long m_value;
        DWORD m_thread_id;
        int m_count;
    };

    /**
     * @class OsSignal
     * @brief Signal in Windows
     *
     * @ingroup Platforms
     */
    class OsSignal : public OsSemaphore
    {
    public:
        OsSignal( ) : OsSemaphore( 0 ) { }
        bool wait( DWORD timeout )
        {
            OsMutex::Scoped protect( &m_mutex );

            // True - wait period is finished, False - otherwise
            return !OsSemaphore::wait( 1, timeout ); 
        }
        void wakeup( ){ OsSemaphore::signal( ); }
    private:
        OsMutex m_mutex;
    };

    /**
     * @class OsTime
     * @brief Millisecond timer
     *
     * @ingroup Platforms
     */
    class OsTime
    {
    public:
        OsTime( ) : m_current( currentTimeMs( ) ) { }

        /**
         * Start counting time
         */
        void start( ){ m_current = currentTimeMs( ); }

        /**
         * Get elapsed time
         *
         * @return Time in milliseconds
         */
        int elapsed( ) { return currentTimeMs( ) - m_current; }

    private:
        DWORD currentTimeMs( ) { return timeGetTime( ); }

    private:
        DWORD m_current;
};
}
#endif
